package com.spzx.product.service.impl;

import com.spzx.common.core.utils.StringUtils;
import com.spzx.product.service.TestService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class TestServiceImpl implements TestService {

   @Autowired
   private StringRedisTemplate stringRedisTemplate;

   /**
    * 释放锁保证原子性
    * @param request
    */
   @Override
   public void testLock(HttpServletRequest request) {

      //1、尝试获取锁 setnx key value
      //没有原子性：先set、再expire
      /*Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
      stringRedisTemplate.expire("lock", 3, TimeUnit.SECONDS);*/
      //具有原子性：在set中直接设置超时时间
      //设置uuid防误删
      String uuid = UUID.randomUUID().toString();
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);

      if(flag){//2、如果获取锁成功
         try {
            //2.1、则执行业务逻辑
            // 查询Redis中的num值
            String value = this.stringRedisTemplate.opsForValue().get("num");

            //发生异常
            /*if(request.getServerPort() == 9205){
               int a = 9 / 0;
            }*/

            // 没有该值return
            if (StringUtils.isBlank(value)){
               return ;
            }
            // 有值就转成int
            int num = Integer.parseInt(value);
            // 把Redis中的num值+1
            this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));

         } catch (NumberFormatException e) {

            System.out.println("发生异常并处理");

         } finally {
            //2.2、释放锁 del key
            //判断是不是当前进程的锁
            /*if(uuid.equals(stringRedisTemplate.opsForValue().get("lock"))){
               //判断语句和释放锁的语句，没有原子性（依然会导致误删其他锁）
               stringRedisTemplate.delete("lock");
            }*/
            //使用lua确保所释放的原子性
            String script = """
                    if redis.call('get', KEYS[1]) == ARGV[1] then
                        return redis.call('del', KEYS[1])
                    else
                        return 0
                    end
                    """;
            Boolean lock = stringRedisTemplate.execute(
                    new DefaultRedisScript<>(script, Boolean.class),
                    Arrays.asList("lock"),
                    uuid);
            System.out.println("释放锁：" + lock);
         }

      }else{//3、如果获取锁失败，则自旋（重试获锁）
         try {
            System.out.println("自旋获取锁");
            Thread.sleep(10);
            this.testLock(request);
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      }
   }


   /**
    * 使用uuid作为锁的value防止误删其他进行的锁
    * @param request
    */
   //@Override
   public void testLockV5(HttpServletRequest request) {

      //1、尝试获取锁 setnx key value
      //没有原子性：先set、再expire
      /*Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
      stringRedisTemplate.expire("lock", 3, TimeUnit.SECONDS);*/
      //具有原子性：在set中直接设置超时时间
      //设置uuid防误删
      String uuid = UUID.randomUUID().toString();
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);

      if(flag){//2、如果获取锁成功
         try {
            //2.1、则执行业务逻辑
            // 查询Redis中的num值
            String value = this.stringRedisTemplate.opsForValue().get("num");

            //发生异常
            if(request.getServerPort() == 9205){
               int a = 9 / 0;
            }

            // 没有该值return
            if (StringUtils.isBlank(value)){
               return ;
            }
            // 有值就转成int
            int num = Integer.parseInt(value);
            // 把Redis中的num值+1
            this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));

         } catch (NumberFormatException e) {

            System.out.println("发生异常并处理");

         } finally {
            //2.2、释放锁 del key
            //判断是不是当前进程的锁
            if(uuid.equals(stringRedisTemplate.opsForValue().get("lock"))){
               //判断语句和释放锁的语句，没有原子性（依然会导致误删其他锁）
               stringRedisTemplate.delete("lock");
            }
         }

      }else{//3、如果获取锁失败，则自旋（重试获锁）
         try {
            System.out.println("自旋获取锁");
            Thread.sleep(10);
            this.testLock(request);
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      }
   }

   /**
    * 为分布式锁添加超时时间
    * @param request
    */
   //@Override
   public void testLockV4(HttpServletRequest request) {

      //1、尝试获取锁 setnx key value
      //没有原子性：先set、再expire
      /*Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
      stringRedisTemplate.expire("lock", 3, TimeUnit.SECONDS);*/
      //具有原子性：在set中直接设置超时时间
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock", 3, TimeUnit.SECONDS);

      if(flag){//2、如果获取锁成功
         try {
            //2.1、则执行业务逻辑
            // 查询Redis中的num值
            String value = this.stringRedisTemplate.opsForValue().get("num");

            //发生异常
            if(request.getServerPort() == 9205){
               int a = 9 / 0;
            }

            // 没有该值return
            if (StringUtils.isBlank(value)){
               return ;
            }
            // 有值就转成int
            int num = Integer.parseInt(value);
            // 把Redis中的num值+1
            this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));

         } catch (NumberFormatException e) {

            System.out.println("发生异常并处理");

         } finally {
            //2.2、释放锁 del key
            stringRedisTemplate.delete("lock");
         }

      }else{//3、如果获取锁失败，则自旋（重试获锁）
         try {
            System.out.println("自旋获取锁");
            Thread.sleep(10);
            this.testLock(request);
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      }
   }

   /**
    * 在finnaly中释放锁，防止死锁
    * @param request
    */
   //@Override
   public void testLockV3(HttpServletRequest request) {

      //1、尝试获取锁 setnx key value
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");

      if(flag){//2、如果获取锁成功
         try {
            //2.1、则执行业务逻辑
            // 查询Redis中的num值
            String value = this.stringRedisTemplate.opsForValue().get("num");

            //发生异常
            if(request.getServerPort() == 9205){
               int a = 9 / 0;
            }

            // 没有该值return
            if (StringUtils.isBlank(value)){
               return ;
            }
            // 有值就转成int
            int num = Integer.parseInt(value);
            // 把Redis中的num值+1
            this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));

         } catch (NumberFormatException e) {

            System.out.println("发生异常并处理");

         } finally {
            //2.2、释放锁 del key
            stringRedisTemplate.delete("lock");
         }

      }else{//3、如果获取锁失败，则自旋（重试获锁）
         try {
            System.out.println("自旋获取锁");
            Thread.sleep(10);
            this.testLock(request);
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      }
   }


   /**
    * 分布式锁：发生异常会导致死锁（锁没有办法被释放）
    * @param request
    */
   //@Override
   public void testLockV2(HttpServletRequest request) {

      //1、尝试获取锁 setnx key value
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");

      if(flag){//2、如果获取锁成功
         //2.1、则执行业务逻辑
         // 查询Redis中的num值
         String value = this.stringRedisTemplate.opsForValue().get("num");

         //发生异常
         if(request.getServerPort() == 9205){
            int a = 9 / 0;
         }

         // 没有该值return
         if (StringUtils.isBlank(value)){
            return ;
         }
         // 有值就转成int
         int num = Integer.parseInt(value);
         // 把Redis中的num值+1
         this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));

         //2.2、释放锁 del key
         stringRedisTemplate.delete("lock");

      }else{//3、如果获取锁失败，则自旋（重试获锁）
         try {
            System.out.println("自旋获取锁");
            Thread.sleep(10);
            this.testLock(request);
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      }
   }

   /**
    * 本地锁：使用synchronized
    */
   //@Override
   public synchronized void testLockV1() {
      // 查询Redis中的num值
      String value = this.stringRedisTemplate.opsForValue().get("num");
      // 没有该值return
      if (StringUtils.isBlank(value)){
         return ;
      }
      // 有值就转成int
      int num = Integer.parseInt(value);
      // 把Redis中的num值+1
      this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));
   }
}